/*
 * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.xuexiang.xui_lib.component.textview.autoFit;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.agp.render.Paint;
import ohos.app.Context;

import java.util.ArrayList;

/**
 * 字体自适应辅助类
 *
 * @author xuexiang
 * @since 2019-05-14 22:10
 */
public class AutoFitHelper {
    /**
     * 最小字体大小，默认是8sp
     */
    private static final int DEFAULT_MIN_TEXT_SIZE = 8;
    /**
     * 默认调整精度（精度值越小，越精准，耗时越长）
     */
    private static final float DEFAULT_PRECISION = 0.5f;

    private Text mTextView;
    private Paint mPaint;
    /**
     * 原始TextView的字体大小
     */
    private int mTextSize;
    /**
     * 字体最大的行数
     */
    private int mMaxLines;
    /**
     * 最小字体大小
     */
    private int mMinTextSize;

    /**
     * 字体大小调整精度
     */
    private float mPrecision;

    /**
     * 是否开启自适应大小
     */
    private boolean mEnabled;
    private boolean mIsAutoFitting;

    private AutoFitHelper(Text view) {
        final Context context = view.getContext();
        mTextView = view;
        mPaint = new Paint();
        setRawTextSize(view.getTextSize());

        mMaxLines = getMaxLines(view);
        mMinTextSize = (int) AttrHelper.getDensity(context) * DEFAULT_MIN_TEXT_SIZE;

        mPrecision = DEFAULT_PRECISION;
    }

    private ArrayList<OnTextSizeChangeListener> mListeners;

    private AutofitTextWatcher mTextWatcher = new AutofitTextWatcher();

    private AutoFitOnLayoutChangeListener mOnLayoutChangeListener = new AutoFitOnLayoutChangeListener();

    /**
     * Creates a new instance of {@code AutofitHelper} that wraps a {@link Text} and enables
     * automatically sizing the text to fit.
     *
     * @param view
     * @return AutoFitHelper
     */
    public static AutoFitHelper create(Text view) {
        return create(view, null);
    }

    /**
     * Creates a new instance of {@code AutofitHelper} that wraps a {@link Text} and enables
     * automatically sizing the text to fit.
     *
     * @param view
     * @param attrs
     * @return AutoFitHelper
     */
    public static AutoFitHelper create(Text view, AttrSet attrs) {
        AutoFitHelper helper = new AutoFitHelper(view);
        boolean enableFit = true;
        if (attrs != null) {
            int minTextSize = (int) helper.getMinTextSize();
            float precision = helper.getPrecision();
            if (attrs.getAttr("aftv_enable").isPresent()) {
                enableFit = attrs.getAttr("aftv_enable").get().getBoolValue();
            }

            if (attrs.getAttr("aftv_minTextSize").isPresent()) {
                minTextSize = attrs.getAttr("aftv_minTextSize").get().getIntegerValue();
            }
            if (attrs.getAttr("aftv_precision").isPresent()) {
                precision = attrs.getAttr("aftv_precision").get().getFloatValue();
            }

            helper.setMinTextSize(minTextSize).setPrecision(precision);
        }
        helper.setEnabled(enableFit);
        return helper;
    }

    /**
     * 对字体大小进行调整
     *
     * @param paint paint
     * @param view view
     * @param minTextSize minTextSize
     * @param maxLines maxLines
     * @param targetWidth targetWidth
     */
    private void getAutoFitTextSize(Paint paint, Text view, int minTextSize, int maxLines, int targetWidth) {
        float textWidth = paint.measureText(view.getText());
        if (textWidth > targetWidth * maxLines) {
            mTextSize--;
            if (mTextSize < minTextSize) {
                mTextSize = minTextSize;
            }
        }
        view.setTextSize(mTextSize);
    }

    private static int getMaxLines(Text view) {
        return view.getMaxTextLines();
    }

    /**
     * 获取最大行数
     *
     * @return mMaxLines
     */
    public int getMaxLines() {
        return mMaxLines;
    }

    /**
     * 添加字体大小变化的监听
     *
     * @param listener
     * @return AutoFitHelper
     */
    public AutoFitHelper addOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
        if (mListeners == null) {
            mListeners = new ArrayList<>();
        }
        mListeners.add(listener);
        return this;
    }

    /**
     * 删除字体大小变化的监听
     *
     * @param listener
     * @return AutoFitHelper
     */
    public AutoFitHelper removeOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
        if (mListeners != null) {
            mListeners.remove(listener);
        }
        return this;
    }

    /**
     * 获取精度
     *
     * @return 字体大小调整精度
     */
    public float getPrecision() {
        return mPrecision;
    }

    /**
     * 设置自适应的调整精度（精度值越小，越精准，耗时越长）
     *
     * @param precision 字体调整精度
     * @return AutoFitHelper
     */
    public AutoFitHelper setPrecision(float precision) {
        if (mPrecision != precision) {
            mPrecision = precision;

            autoFit();
        }
        return this;
    }

    /**
     * 获取最小字体大小【单位： px】
     *
     * @return float
     */
    public float getMinTextSize() {
        return mMinTextSize;
    }

    /**
     * 设置自适应的最小字体大小【单位：sp】
     *
     * @param size 字体大小
     * @return AutoFitHelper
     */
    public AutoFitHelper setMinTextSize(int size) {
        setRawMinTextSize(size);
        return this;
    }

    private void setRawMinTextSize(int size) {
        if (size != mMinTextSize) {
            mMinTextSize = size;
            autoFit();
        }
    }

    /**
     * 设置行数
     *
     * @param lines
     * @return AutoFitHelper
     */
    public AutoFitHelper setMaxLines(int lines) {
        if (mMaxLines != lines) {
            mMaxLines = lines;
            autoFit();
        }
        return this;
    }

    /**
     * 是否开启自适应
     *
     * @return 是否开启自适应字体大小的功能
     */
    public boolean isEnabled() {
        return mEnabled;
    }

    /**
     * 设置是否开启自适应字体大小的功能
     *
     * @param enabled 是否开启
     * @return AutoFitHelper
     */
    public AutoFitHelper setEnabled(boolean enabled) {
        if (mEnabled != enabled) {
            mEnabled = enabled;
            if (enabled) {
                mTextView.addTextObserver(mTextWatcher);
                mTextView.setLayoutRefreshedListener(mOnLayoutChangeListener);
                autoFit();
            } else {
                mTextView.removeTextObserver(mTextWatcher);
                mTextView.setLayoutRefreshedListener(null);
                mTextView.setTextSize(mTextSize);
            }
        }
        return this;
    }

    /**
     * Returns the original text size of the View.
     *
     * @return float
     */
    public float getTextSize() {
        return mTextView.getTextSize();
    }

    /**
     * Set the original text size of the View.
     *
     * @param size
     */
    public void setTextSize(int size) {
        if (mIsAutoFitting) {
            // We don't want to update the TextView's actual textSize while we're autofitting
            // since it'd get set to the autofitTextSize
            return;
        }
        setRawTextSize(size);
    }

    private void setRawTextSize(int size) {
        if (mTextSize != size) {
            mTextSize = size;
        }
    }

    /**
     * 对字体大小进行调整
     *
     * @param view view
     * @param paint paint
     * @param minTextSize minTextSize
     * @param maxLines maxLines
     */
    private void autoFit(Text view, Paint paint, int minTextSize, int maxLines) {
        if (maxLines <= 0) {
            // Don't auto-size since there's no limit on lines.
            return;
        }

        int targetWidth = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight();
        if (targetWidth <= 0) {
            return;
        }
        paint.setColor(view.getTextColor());
        paint.setAntiAlias(true);
        paint.setFont(view.getFont());
        paint.setTextSize(mTextSize);
        getAutoFitTextSize(paint, view, minTextSize, maxLines, targetWidth);
    }

    private void autoFit() {
        int oldTextSize = mTextView.getTextSize();
        int textSize;
        mTextSize = mTextView.getTextSize();

        mIsAutoFitting = true;
        autoFit(mTextView, mPaint, mMinTextSize, mMaxLines);
        mIsAutoFitting = false;

        textSize = mTextView.getTextSize();

        if (textSize != oldTextSize) {
            sendTextSizeChange(textSize, oldTextSize);
        }
    }

    private void sendTextSizeChange(float textSize, float oldTextSize) {
        if (mListeners == null) {
            return;
        }

        for (OnTextSizeChangeListener listener : mListeners) {
            listener.onTextSizeChange(textSize, oldTextSize);
        }
    }

    private class AutofitTextWatcher implements Text.TextObserver {
        @Override
        public void onTextUpdated(String s, int i, int i1, int i2) {
            autoFit();
        }
    }

    private class AutoFitOnLayoutChangeListener implements Component.LayoutRefreshedListener {
        @Override
        public void onRefreshed(Component component) {
            autoFit();
        }
    }

    /**
     * 文字大小变化的监听
     */
    public interface OnTextSizeChangeListener {
        /**
         * 文字大小发生变化
         *
         * @param newTextSize 新的字体大小
         * @param oldTextSize 旧的字体大小
         */
        void onTextSizeChange(float newTextSize, float oldTextSize);
    }
}
